package com.microcore.service;

import org.quartz.*;
import org.quartz.utils.Key;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.microcore.dao.mapper.InvokeTriggerMapper;
import com.microcore.entity.InvokeTrigger;
import com.microcore.entity.UpdateTrigger;
import com.microcore.entity.param.InvokeMethodParams;
import com.microcore.job.DynamicJobConcurrent;
import com.microcore.job.DynamicJobSerialization;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Descripttion 定时任务调度方法
 * @Author LSY
 * @Date 2018/10/15 15:48
 **/
@Service("mcJobService")
public class JobService {

	private static Logger logger = LoggerFactory.getLogger(JobService.class);
	
	public static ExecutorService pool = Executors.newFixedThreadPool(10);
	
	@Autowired
	private SchedulerFactoryBean factory;
	@Autowired
	private InvokeTriggerMapper triggerMapper;

    /**
     * @Descripttion 获取JobDetail,JobDetail是任务的定义,而Job是任务的执行逻辑,JobDetail里会引用一个Job Class来定义,不可以并发执行
     * @Author LSY
     * @Date 2018/10/15 15:13
     * @Param [jobKey, description, map]
     * @Return
     **/
    public JobDetail getJobDetailSerialization(JobKey jobKey, JobDataMap map) {
        return JobBuilder.newJob(DynamicJobSerialization.class)
                .withIdentity(jobKey)
                .setJobData(map)
                .storeDurably()
                .build();
    }
    
    /**
     * 支持并发
     * @param jobKey
     * @param map
     * @return
     */
    public JobDetail getJobDetailConcurrent(JobKey jobKey, JobDataMap map) {
        return JobBuilder.newJob(DynamicJobConcurrent.class)
                .withIdentity(jobKey)
                .setJobData(map)
                .storeDurably()
                .build();
    }

    /**
     * 构建按指定时间和制定次数执行的Trigger以及指定时间内无限循环的trigger
     * @author yegui  
     * @date 2019年1月30日  
     * @param trigger
     * @param jobDetail
     * @return
     * @throws Exception
     * Trigger
     */
    public Trigger getPlanTrigger(InvokeTrigger trigger, JobDetail jobDetail) throws Exception {
    	Trigger simpleTrigger = null;
    	/*if (trigger.getEndDatetime().getTime() < new Date().getTime()) {
    		throw new Exception("计划时间异常，修改失败");
    	}*/
    	if ((trigger.getCount() == 0 || trigger.getCount() == null) && trigger.getInterval() != null) {
    		simpleTrigger = TriggerBuilder.newTrigger()
                    .withIdentity(trigger.getTriggerName())
                    .startAt(trigger.getStartDatetime())
                    .endAt(trigger.getEndDatetime())
                    .forJob(jobDetail)
                    .withSchedule(SimpleScheduleBuilder
                    		.simpleSchedule()
                    		.withIntervalInSeconds(trigger.getInterval())
                    		.repeatForever())
                    .build();
    	} else if (trigger.getCount() > 0 && trigger.getInterval() != null) {
    		simpleTrigger = TriggerBuilder.newTrigger()
                    .withIdentity(trigger.getTriggerName())
                    .startAt(trigger.getStartDatetime())
                    .endAt(trigger.getEndDatetime())
                    .forJob(jobDetail)
                    .withSchedule(SimpleScheduleBuilder
                    		.simpleSchedule()
                    		.withIntervalInSeconds(trigger.getInterval())
                    		.withRepeatCount(trigger.getCount() - 1))
                    .build();
    	}
    	if (null == simpleTrigger) {
    		throw new Exception("计划时间异常，修改失败");
    	}
    	return simpleTrigger;
    	
    }
    
    /**
     * 开始执行计划
     * @param scheduler
     * @param className
     * @param methodName
     * @param isConcurrent
     * @param isRestart
     * @param params
     * @throws Exception 
     */
    public void startInvoke(Scheduler scheduler, String className, String methodName, boolean isConcurrent, 
    		boolean isRestart, String fkId) throws Exception {
    	List<InvokeTrigger> triggers = triggerMapper.getTriggersByInvokeId(fkId);
    	CountDownLatch counter = new CountDownLatch(triggers.size());
    	for (InvokeTrigger trigger : triggers) {
    		pool.execute(new ScheduleThread(scheduler, isRestart, className, methodName, isConcurrent, trigger));
    	}
    	counter.countDown();
		scheduler.start();
    }
    
    /**
     * 重新绑定Trigger
     * @param updateTrigger
     * @throws Exception
     */
    public void updateTriggerKey(UpdateTrigger updateTrigger) throws Exception {
    	Scheduler scheduler = factory.getScheduler();
		List<InvokeMethodParams> methodParams = JSON.parseArray(updateTrigger.getInvokeTrigger().getParams(), InvokeMethodParams.class);
    	// 根据调度名称和group获取jobKey(group为默认的)
		JobKey jobKey = getJobKey(updateTrigger.getInvokeTrigger().getInvokeTriggerId());
		
		JobDataMap map = new JobDataMap();
		setParams(updateTrigger.getClassName(), updateTrigger.getMethodName(), methodParams, map);
		// 获取JobDetail对象
		JobDetail jobDetail = null;
		if (updateTrigger.isConcurrent()) {
			jobDetail = getJobDetailConcurrent(jobKey, map);
		} else {
			jobDetail = getJobDetailSerialization(jobKey, map);
		}
		scheduler.addJob(jobDetail, true);
		scheduler.scheduleJob(getPlanTrigger(updateTrigger.getInvokeTrigger(), jobDetail));
		logger.debug("更新trigger成功");
    }
    
    /**
     * 移除trigger
     * @param fkId
     * @throws Exception
     */
    public void deleteTriggerKey(String fkId) throws Exception {
    	Scheduler scheduler = factory.getScheduler();
		List<InvokeTrigger> triggers = triggerMapper.getTriggersByInvokeId(fkId);
		for (InvokeTrigger trigger : triggers) {
			TriggerKey triggerKey = new TriggerKey(trigger.getInvokeTriggerId(), Key.DEFAULT_GROUP);
			scheduler.unscheduleJob(triggerKey);// 移除触发器
		}
    }
    
    /**
     * 
     * 
     * @author yegui  
     * @date 2019年1月30日
     */
    class ScheduleThread implements Runnable {
    	
    	private final String classPath;

    	private final String methodName;

    	private JobService jobService = new JobService();
    	
    	private InvokeTrigger trigger;

    	private Scheduler scheduler;

    	//是否为重启
    	private boolean isRestart;
    	//是否并发执行
    	private boolean isConcurrent;
    	
    	private List<InvokeMethodParams> params ;

    	public ScheduleThread(Scheduler scheduler, boolean isRestart, String classPath, 
    			String methodName, boolean isConcurrent, InvokeTrigger trigger) {
    		this.scheduler = scheduler;
    		this.isRestart = isRestart;
    		this.classPath = classPath;
    		this.methodName = methodName;
    		this.isConcurrent = isConcurrent;
    		this.params = JSON.parseArray(trigger.getParams(), InvokeMethodParams.class);
    		this.trigger = trigger;
    	}

    	@Override
    	public void run() {
    		try {
    			// 根据调度名称和group获取jobKey(group为默认的)
    			JobKey jobKey = getJobKey(trigger.getInvokeTriggerId());
    			if (isRestart) {
    				// 根据调度名称和group获取TriggerKey(group为默认的)
    				TriggerKey triggerKey = getTriggerKey(trigger.getInvokeTriggerId());
    				scheduler.unscheduleJob(triggerKey);// 移除触发器
    				scheduler.deleteJob(jobKey); // 删除任务
    			}
    			JobDataMap map = new JobDataMap();
    			setParams(classPath, methodName, params, map);
    			JobDetail jobDetail = null;
    			// 获取JobDetail对象
    			if (isConcurrent) {
    				jobDetail = jobService.getJobDetailConcurrent(jobKey, map);
    			} else {
    				jobDetail = jobService.getJobDetailSerialization(jobKey, map);
    			}
    			
    			// 将jobdetail和trigger关联并注入到管理类
    			scheduler.addJob(jobDetail, true);
    			Trigger simpleTrigger = jobService.getPlanTrigger(trigger, jobDetail);
    			if (simpleTrigger != null) {
    				scheduler.scheduleJob(simpleTrigger);
    			}
    		} catch (Exception e) {
    			logger.error("执行调度的线程报错，" + e.getMessage(), e);
    		}
    	}
    }
    
    /*
     * 设置job参数
     * @author yegui  
     * @date 2019年1月30日  
     * @param className
     * @param methodName
     * @param params
     * @param map
     * @return
     * JobDataMap
     */
    private JobDataMap setParams(String className, String methodName, List<InvokeMethodParams> params, JobDataMap map) {
		map.put("className", className);
		map.put("methodName", methodName);
		List<String> sortList = new ArrayList<>();
		params.forEach(ele->{
			map.put(ele.getKey(), ele.getValue());
			sortList.add(ele.getKey());
		});
		map.put("sortArray", sortList.toArray());
		return map;
	}
    
    /*
     * 获取jobKey 
     * @author yeGui  
     * @date 2019年1月30日  
     * @param id
     * @return
     * JobKey
     */
    private JobKey getJobKey(String id) {
		return JobKey.jobKey(id, Key.DEFAULT_GROUP);
	}
	
    /*
     * 获取triggerKey
     * @author yegui  
     * @date 2019年1月30日  
     * @param id
     * @return
     * TriggerKey
     */
    private TriggerKey getTriggerKey(String id) {
		return TriggerKey.triggerKey(id, Key.DEFAULT_GROUP);
	}

}
